{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "118a0c60-5c34-4db8-871b-da1f8efe1194",
   "metadata": {},
   "source": [
    "# Generative AI and Large Language Models\n",
    "\n",
    "In this chapter we will explore recipes that use the generative aspect of the transformer models to generate text. As we touched upon the same in the `Transformers and its applications` chapter, the generative aspect of the transformer models uses the decoder component of the tranformer network. The decoder component is responsible for generating text based on the provided context.\n",
    "\n",
    "With the advent of the GPT (General Purpose Transformers) family of large language models (LLMs), these have only grown in size and capability with each new version. Large language models like GPT-4 have been trained on large corpora of text and can match or beat the state-of-the-art on many NLP tasks. These LLMs have also build upon their generational capability and they can be instructed to generate text based on human prompting.\n",
    "\n",
    "We will use generative models based on the transformer architecture for our recipes and explore:\n",
    "\n",
    "* How to run a generative model or a LLM locally on our system\n",
    "* How to make the LLM follow instruction prompting to generate text\n",
    "* How to augment the LLM with custom data to answer questions based on it\n",
    "* How to use different types of LLMs to generate code, SQL etc.\n",
    "* How to use agents to perform actions to make decisions via an LLM and augment it to answer questions.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b68099d3-d6b4-4dae-a066-9ec5b3780df6",
   "metadata": {},
   "source": [
    "\n",
    "# <h1><center>Running an LLM locally</center></h1>\n",
    "\n",
    "In this recipe, we will learn how to load a LLM locally using the CPU or GPU and generate text from it after giving it a starting text as seed input. We recommend that you use a system with at least 16 GB of RAM, or a system with a GPU that has at least 8 GB of VRAM. These examples were created on a system with 8 GB of RAM and an nVidia RTX 2070 GPU with 8 GB of VRAM. These examples will work without a GPU as long as there is 16 GB of RAM."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b8550ce-4428-4cb1-bdd9-e405019ed19b",
   "metadata": {},
   "source": [
    "## How to do it\n",
    "\n",
    "In this recipe, we will load the `Mistral-7B` model using the HuggingFace libraries. This model has a smaller size compared to other language models in its class, but can outperform them on several NLP tasks. The `Mistral-7B` model with 7 billion parameters can outperform the `Llama 2` model that has over 13 billion parameters. For more information on this, please refer to the Mistral model details on https://mistral.ai/news/announcing-mistral-7b/."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbfcba3d-40a6-4589-ac5e-0c677566488d",
   "metadata": {},
   "source": [
    "1. Do the necessary imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95587402-30a9-4c90-8de8-3270a001c007",
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig\n",
    "import torch\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fe71513-0a68-4a7c-a10f-42a645ea85a6",
   "metadata": {},
   "source": [
    "2. Load the Mistral model `mistralai/Mistral-7B-v0.1` and the respective tokenizer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b62fa69-df7f-4c44-8f66-3fd13eaa1932",
   "metadata": {},
   "outputs": [],
   "source": [
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "model = AutoModelForCausalLM.from_pretrained(\n",
    "            \"mistralai/Mistral-7B-v0.1\", device_map=\"auto\", load_in_4bit=True\n",
    "        )\n",
    "\n",
    "tokenizer = AutoTokenizer.from_pretrained(\"mistralai/Mistral-7B-v0.1\", padding_side=\"left\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c6ae8f1-afac-4881-bfdc-3ec6437bb5bf",
   "metadata": {},
   "source": [
    "3. Initialize a generation config. This generation config is passed to the model instructing it on how to generate the text."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "da87fc62-5d12-426d-a31a-98bdaf63775d",
   "metadata": {},
   "outputs": [],
   "source": [
    "generation_config = GenerationConfig(\n",
    "    num_beams=4,\n",
    "    early_stopping=True,\n",
    "    eos_token_id=model.config.eos_token_id,\n",
    "    pad_token_id=model.config.eos_token_id,\n",
    "    max_new_tokens=900,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2be9cc06-c1c2-4a11-b175-d2bcc60f683d",
   "metadata": {},
   "source": [
    "4. Initialize a seed sentence. In this case, we ask the LLM to tell us a step by step process to make an apple pie."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0db5e074-1192-4c22-8f68-023bae2e2c06",
   "metadata": {},
   "outputs": [],
   "source": [
    "seed_sentence = \"Step by step way on how to make an apple pie:\"\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b58fe42-945d-4e6e-893e-a1fcfb11e413",
   "metadata": {},
   "source": [
    "5. Tokenize the seed sentence and get the model to generate the tokens IDs as the model output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "17b44aa9-19e7-4e85-91d0-1fc400c2f472",
   "metadata": {},
   "outputs": [],
   "source": [
    "model_inputs = tokenizer([seed_sentence], return_tensors=\"pt\").to(device)\n",
    "generated_ids = model.generate(**model_inputs, generation_config=generation_config)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6049933-6479-4c65-94f4-551eebbbace3",
   "metadata": {},
   "source": [
    "6. Decode the model token IDs into text and print them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b0034bd3-96bf-4786-8368-f174217323e3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step by step way on how to make an apple pie:\n",
      "\n",
      "1. Preheat the oven to 200°C/400°F/gas mark 6.\n",
      "2. Roll out the pastry on a lightly floured surface to the thickness of a £1 coin.\n",
      "3. Line a 23cm/9in fluted tart tin with the pastry, leaving the excess pastry hanging over the edge.\n",
      "4. Prick the base of the pastry with a fork.\n",
      "5. Line the pastry with baking parchment and fill with baking beans.\n",
      "6. Bake for 15 minutes.\n",
      "7. Remove the baking beans and baking parchment.\n",
      "8. Bake for a further 10 minutes, or until the pastry is golden-brown.\n",
      "9. Remove from the oven and leave to cool.\n",
      "10. Reduce the oven temperature to 180°C/350°F/gas mark 4.\n",
      "11. Peel, core and slice the apples.\n",
      "12. Place the apples in a large bowl.\n",
      "13. Add the sugar, lemon juice and cinnamon.\n",
      "14. Mix well.\n",
      "15. Spoon the apple mixture into the pastry case.\n",
      "16. Roll out the remaining pastry on a lightly floured surface to the thickness of a £1 coin.\n",
      "17. Cut the pastry into 2.5cm/1in strips.\n",
      "18. Weave the pastry strips over the apples.\n",
      "19. Brush the pastry with the beaten egg.\n",
      "20. Bake in the oven for 30-35 minutes, or until the pastry is golden-brown.\n",
      "21. Remove from the oven and leave to cool for 10 minutes.\n",
      "22. Cut into slices and serve.\n",
      "\n",
      "Ingredients:\n",
      "\n",
      "- 375g/13oz ready-made shortcrust pastry\n",
      "- 500g/1lb 2oz Bramley apples, peeled, cored and sliced\n",
      "- 100g/3½oz caster sugar\n",
      "- 1 tbsp lemon juice\n",
      "- ½ tsp ground cinnamon\n",
      "- 1 free-range egg, beaten, to glaze\n"
     ]
    }
   ],
   "source": [
    "generated_tokens = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]\n",
    "print(generated_tokens)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d9d4f37-bbed-4545-ab84-f4204dc45d26",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "In step 1, we do the necessary imports.\n",
    "\n",
    "In step 2, we initialize the device, the `mistralai/Mistral-7B-v0.1` model and tokenizer respectively. We set the *device_map* parameter to *auto*, which lets the pipeline pick the available device to use. We use the *load_in_4bit* parameter to *True*. This lets us load the quantized model for the inference (or generation) step. Using a quantized model consumes less memory and lets us load the model locally on systems with limited memory. The loading of quantized model is handled by the *AutoModelForCausalLM* module and it downloads a model from the HuggingFace hub that has been quantized to the bit-size specified in the parameter.\n",
    "\n",
    "In step 3, we initialize the text-generation configuration. We use the *num_beams* parameter to be 4. This parameter results in the generated text being more coherent and grammatically correct as the number of beams are increased. However, more number of beams also result in decoding (or text-generation) time. We set the *early_stopping* parameter to *True* as the generation of the next word is concluded as soon as the number of beams reaches the value specified in the *num_beams* parameter. The *eos_token_id* and *pad_token_id* are defaulted to use the models token IDs. These token IDs are used to specify the end-of-sentence and padding tokens that will be used by the model. The *max_new_tokens* parameter specifies the maximum number of tokens that will be generated. There are more parameters that can be specified for generating the text and we encourage the readers to play around with different values of the above specified parameters as well as any additional parameter for customizing the text generation. For more information, please refer to the transformer documentation on the GenerationConfig class at *https://github.com/huggingface/transformers/blob/main/src/transformers/generation/configuration_utils.py*.\n",
    "\n",
    "In step 4, we initialize a seed sentence. This sentence acts as a prompt to the model for it to generate a step-by-step way to make an apple pie. As we saw in the output above, the model did generate a step-by-step recipe with proper numbering and arrangement of the text for the ingredients too.\n",
    "\n",
    "In step 5, we tokenized the seed sentence to transform the text into the corresponding embedded representation and pass it to the model to generate the text. We also pass the *generation_config* instance to it. The model generates the token IDs as part of its generation.\n",
    "\n",
    "In step 6, we decode the token IDs that were generated from the previous step. We set the value of the *skip_special_tokens* to *True*. The transformer model uses special tokens like *CLS* or *MASK* and to generate the text as part of the training. We don't want these to be in the output as we want to generate pure text as part of our output. We print the decoded (or generated) text.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e00ef98-3ebc-4a07-9f67-f7fb061b90e1",
   "metadata": {},
   "source": [
    "# <h1><center>Running an LLM to follow instructions</center></h1>\n",
    "\n",
    "In this recipe, we will learn how to get an LLM to follow instructions via prompting. We will use the `mistralai/Mistral-7B-Instruct-v0.1` model for this recipe. This model is build on top of the `mistralai/Mistral-7B-v0.1` LLM and has been tuned to follow instructions via prompts. Let's get started.\n",
    "\n",
    "## How to do it"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "655cd405-76c6-4a18-9cdc-77870b35d602",
   "metadata": {},
   "source": [
    "1. Do the necessary imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f59cfbb-f324-458f-82fb-3cae1460f6a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForCausalLM, AutoTokenizer, GenerationConfig, logging\n",
    "import torch"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "347b6837-4dc8-42fc-88ef-25939ff4a78e",
   "metadata": {},
   "source": [
    "2. Set the logging level and device."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "d53d018d-93b2-4d1d-a2c9-405de13e6727",
   "metadata": {},
   "outputs": [],
   "source": [
    "logging.set_verbosity_error()\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7e2eefa-3812-46a4-8bc8-010c2a18c912",
   "metadata": {},
   "source": [
    "3. Load the Mistral model `mistralai/Mistral-7B-Instruct-v0.1` and the corresponding tokenizer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34627d66-6a21-48b6-a9f0-78cde51dfccf",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = AutoModelForCausalLM.from_pretrained(\"mistralai/Mistral-7B-Instruct-v0.1\", device_map=\"auto\", \n",
    "                                             load_in_4bit=True, torch_dtype=torch.float16)\n",
    "tokenizer = AutoTokenizer.from_pretrained(\"mistralai/Mistral-7B-Instruct-v0.1\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acbb5b9f-0909-4ad6-8828-0d58e4d7feca",
   "metadata": {},
   "source": [
    "4. Create a prompt that sets up an instruction context that can be passed to the LLM. The LLM acts as per the instructions setup in the prompt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4c1c9ed6-93ab-46eb-9b0d-9045daeac2f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = [\n",
    "    {\"role\": \"user\", \"content\": \"What is your favourite country?\"},\n",
    "    {\"role\": \"assistant\", \"content\": \"Well, I am quite fascinated with Peru.\"},\n",
    "    {\"role\": \"user\", \"content\": \"What can you tell me about Peru?\"}\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a0c31e8-3c6b-477d-ae99-bfdef4a34fb3",
   "metadata": {},
   "source": [
    "5. Set the generation config for the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "14478d3a-ab5f-4721-92ce-3cabf5de9961",
   "metadata": {},
   "outputs": [],
   "source": [
    "generation_config = GenerationConfig(\n",
    "    num_beams=4,\n",
    "    early_stopping=True,\n",
    "    eos_token_id=model.config.eos_token_id,\n",
    "    pad_token_id=model.config.eos_token_id,\n",
    "    max_new_tokens=900,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c523633d-daad-4ce1-8eb7-e0ffab08086c",
   "metadata": {},
   "source": [
    "6. Tokenize the prompt load it into the device as setup in step 2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2bb46d92-5bd1-48aa-b596-dbed444539c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "encoded_prompt = tokenizer.apply_chat_template(prompt, return_tensors=\"pt\")\n",
    "model_inputs = encoded_prompt.to(device)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5edf1b89-3ab9-4cfb-b792-82f65d106386",
   "metadata": {},
   "source": [
    "7. Call the *generate* method on the model to generate the output based on the tokenized input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7bc5d21f-23c1-4dd9-9e92-07db27122193",
   "metadata": {},
   "outputs": [],
   "source": [
    "generated_ids = model.generate(model_inputs, max_new_tokens=1000, do_sample=True,\n",
    "                               generation_config=generation_config)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "951c0aad-52e1-4fdd-8164-c599aea3a5f8",
   "metadata": {},
   "source": [
    "8. Decode the output token IDs into text and print it. We can see that the LLM generated the content about Peru in a well articulated way."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b6272d9a-4b18-4622-9624-2a3e57220d6d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<s> [INST] What is your favourite country? [/INST]Well, I am quite fascinated with Peru.</s>  [INST] What can you tell me about Peru? [/INST] Peru is a country located in South America, and it is the 20th largest country in the world by land area. It is bordered by Ecuador to the north, Colombia to the northeast, Brazil to the east, Bolivia and Chile to the south, and the Pacific Ocean to the west.\n",
      "\n",
      "Peru is known for its diverse geography and culture, with a range of landscapes including the Andes Mountains, the Amazon Rainforest, and the Pacific coastline. The country is also home to a number of ancient civilizations, including the Inca Empire, which was once the largest empire in pre-Columbian America.\n",
      "\n",
      "Peru is also known for its rich biodiversity, with a wide variety of flora and fauna found throughout the country. The Amazon Rainforest, in particular, is home to a vast array of plant and animal species, many of which are found nowhere else on Earth.\n",
      "\n",
      "In terms of its economy, Peru is one of the fastest-growing countries in South America, with a strong focus on mining, agriculture, and tourism. The country is also home to a number of world-class archaeological sites, including Machu Picchu, which is considered one of the Seven Wonders of the Ancient World.\n",
      "\n",
      "Overall, Peru is a fascinating country with a rich history, diverse culture, and stunning natural beauty.</s>\n"
     ]
    }
   ],
   "source": [
    "decoded = tokenizer.batch_decode(generated_ids)\n",
    "print(decoded[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccf7cb1a-7a55-4738-b379-bdb7ef7cd880",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "In step 1, we do the necessary imports.\n",
    "\n",
    "In step 2, we set the logging setting to error and lower its verbosity to keep the example output concise. We also initialize the device which in our case is *cuda*. \n",
    "\n",
    "In step 3, we initialize the `mistralai/Mistral-7B-Instruct-v0.1` model and the corresponding tokenizer. This model is based on the model `mistralai/Mistral-7B-v0.1` that we used in the previous recipe. This model has been fine-tuned for QA and interactive conversion. \n",
    "\n",
    "In step 4, we setup the prompt based on the model chat template. The prompt sets up an instruction for the model to follow. This chat template is used by the tokenizer in the subsequent step.\n",
    "\n",
    "In step 5, we initialize the generation configuration similar to the previous recipe.\n",
    "\n",
    "In step 6, we call the *apply_chat_template* method of the tokenizer with the prompt. This method reads the tokenizer's chat_template to ascertain the control tokens and format to use to tokenize the input accordingly. We load the tokenized input into the device.\n",
    "\n",
    "In step 7, we generate the output for the given tokenized chat template input.\n",
    "\n",
    "In step 8, we decode the token IDs returned by the model and print the result. As we observe from the output, the printed output also printed out the instruction that was sent to the model. The instruction follows the format of \n",
    "`<s>[INST] Instruction [/INST] Model answer</s>[INST] Follow-up instruction [/INST]`. The `<s>` and `<\\s>` token specify the beginning and the end of the sentence respectively. The `[INST]` and `[/INST]` are the start and end of the instruction. In this particular case, we started our instruction with a conversation between the user and the agent. The conversation started with the question *What is your favourite country?*. This question was followed by the model answer in the form of *Well, I am quite fascinated with Peru.*. We then followed it up with another instruction by asking the question *What can you tell me about Peru?*. This methodology serves as a template for the LLM to learn our intent and generate an answer for the follow-up question based on the pattern we specified in our instruction prompt.\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e693e0a5-b219-410a-a24c-6229f81c09a2",
   "metadata": {},
   "source": [
    "# There's more\n",
    "\n",
    "Now that we have seen a way to instruct a model to generate text, we can just change the prompt and get the model to generate text for a completely different kind of question. Let's just change the prompt text to the following and use the same recipe to generate text based on the updated prompt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "e576c1ab-3b76-43f0-87fc-9b76d4613efc",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = [\n",
    "    {\"role\": \"user\", \"content\": \"Mary is twice as old as Sarah presently. Sarah is 6 years old.?\"},\n",
    "    {\"role\": \"assistant\", \"content\": \"Well, what can I help you with?\"},\n",
    "    {\"role\": \"user\", \"content\": \"Can you tell me in a step by step way on how old Mary will be after 5 years?\"}\n",
    "]"
   ]
  },
  {
   "cell_type": "raw",
   "id": "aa4e5128-80a1-44be-b2f0-01d86747367f",
   "metadata": {},
   "source": [
    "<s> [INST] Mary is twice as old as Sarah presently. Sarah is 6 years old.? [/INST]Well, what can I help you with?</s>  [INST] Can you tell me in a step by step way on how old Mary will be after 5 years? [/INST] Sure, here's a step-by-step calculation:\n",
    "\n",
    "1. Sarah is currently 6 years old.\n",
    "2. Mary is twice as old as Sarah, so Mary is currently 6 x 2 = 12 years old.\n",
    "3. After 5 years, Mary will be 12 + 5 = 17 years old.\n",
    "\n",
    "So, Mary will be 17 years old after 5 years.</s>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c0cbddb-1841-49cd-9955-ae8f0175cfc5",
   "metadata": {},
   "source": [
    "As we can see from the above output, the instruct model is able to understand the instruction quite clearly and is able to reason well and answered the question correctly. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d6ea91d-bb4a-41c9-8cdf-e369e469014f",
   "metadata": {},
   "source": [
    "# <h1><center>Augmenting an LLM with external data</center></h1>\n",
    "\n",
    "In the following recipres, we will learn how to get an LLM to answer questions on which it has not been trained on. These could include information that was created after the LLM was trained. The content on the world wide web keeps getting added on a daily basis. There is no one LLM can be trained on that context everyday. The Retriever Augmented Generation or RAG frameworks allow us to augment the LLM with additional content that can be sent as input to it for generating content for downstream tasks. As a basic introduction to RAG, we will augment an LLM with some content from a few web pages and ask some questions pertaining to the content contained in those pages. For this recipe, we will first load the LLM and ask it a few questions without providing it any context. We will then augment this LLM with additional context and ask the same questions. We will compare and contrast the answers, which will demonstrate the power of the LLM when coupled with augmented content."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0aa69c3-67d6-489b-8afe-808858237615",
   "metadata": {},
   "source": [
    "# <h1><center>Execute a simple prompt-to-LLM chain</center></h1>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c5c9d3e-e53c-472e-aa21-146821723a11",
   "metadata": {},
   "source": [
    "In this recipe we will use the Langchain *https://www.langchain.com/* framework to demonstrate Langchain framework with an example based on Langchain Expression Language (LCEL). We recommend that the readers visit the specified URL and install the python libraries by following the documentation on the site. In general, the command `pip install langchain` should be sufficient to get started. Let's start with a simple recipe based on the Langchain framework and extend it in the recipes that follow from there on.\n",
    "\n",
    "So far in the book, we loaded all the models as part of the recipes. For the Langchain based recipes, we will improvise a bit on the model loading by storing them as out-of-process. This enables us to decouple to recipe code from the model loading code. This also brings in the additional benefits of faster recipe execution since the model is always resident in memory while we execute the recipe.\n",
    "\n",
    "\n",
    "## How to do it\n",
    "\n",
    "For the upcoming recipes, we will follow the Ollama framework (https://github.com/jmorganca/ollama) to load the models out of process. Follow the steps below to install the Ollama artifacts and run the `llama2` model. The following steps are for the Linux and WSL2 environments. For other OSes, please refer to the documentation on the site."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c7617e6-117f-4c21-abbb-09d42afa843f",
   "metadata": {},
   "source": [
    "1. Execute the following command and install the necessary modules."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a505d66-655f-40c1-acde-995e328af6c0",
   "metadata": {},
   "source": [
    "`curl https://ollama.ai/install.sh | sh`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50f47461-69f4-4abb-9b63-836889c074d0",
   "metadata": {},
   "source": [
    "2. Run the `llama2` model locally by executing the following command."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4290c2eb-46bb-42e3-a616-baa8f727c39b",
   "metadata": {},
   "source": [
    "`ollama run llama2`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbbde019-12df-43a1-804c-0023ecaf6c1c",
   "metadata": {},
   "source": [
    "3. Once the model is up and running, we can now start with the recipe. Start with doing the necessary imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "471a5402-4745-400f-baa2-db4c8a823919",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.llms import Ollama\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0510260a-ae9e-43b4-8b35-e770444a6e70",
   "metadata": {},
   "source": [
    "4. Initialize the model ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "570d9873-ebf6-42dd-bf57-20e122780de0",
   "metadata": {},
   "outputs": [],
   "source": [
    "MODEL_NAME = \"llama2\"\n",
    "llm = Ollama(model=MODEL_NAME)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53a66f32-25ce-4821-8b7f-08983bf946f5",
   "metadata": {},
   "source": [
    "5. We initialize a chat prompt template which is of the defined type `ChatPromptTemplate`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e2e96768-8a8a-40b6-bcfc-b757759c9d9c",
   "metadata": {},
   "outputs": [],
   "source": [
    "        \n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"You are a great mentor.\"),\n",
    "    (\"user\", \"{input}\")\n",
    "])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd9394dc-0871-4729-b0b0-e8f80a7b171b",
   "metadata": {},
   "source": [
    "6. We initialize an output parser that is of the type `StrOutputParser`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "5acd9f85-cbd6-4476-afb5-b2d4f6dac7f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "output_parser = StrOutputParser()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b541221c-ec9a-489a-b027-f45cabc4c44d",
   "metadata": {},
   "source": [
    "7. We initialize an instance of a chain. This chain passes the prompt to the llm, and the output of the llm is passed to the output parser."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "73cec462-5cd2-4dfc-b90f-32a7be50985a",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = prompt | llm | output_parser"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba851459-2db3-495d-867b-3c7f6cf91ca1",
   "metadata": {},
   "source": [
    "8. We create a simple query and ask the LLM to answer it. As we observe from the output, the advice presented by this example is actually a good one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7be201c0-da34-4830-9278-91f055195fbf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "As a mentor, I'm glad you asked! Improving your software engineering skills is an ongoing process, and there are several ways to do so. Here are some suggestions:\n",
      "\n",
      "1. Practice, practice, practice: The more you work on actual projects, the better you'll become at software engineering. Look for opportunities to contribute to open-source projects or volunteer to work on small projects for friends or family.\n",
      "2. Learn new programming languages and technologies: Stay up-to-date with the latest tools and trends in the industry by learning new programming languages, frameworks, and tools.\n",
      "3. Focus on problem-solving skills: Good software engineers are able to break down complex problems into smaller, manageable parts and come up with creative solutions. Practice solving problems on platforms like HackerRank or LeetCode to improve your problem-solving skills.\n",
      "4. Learn about software design patterns: Understanding software design patterns can help you write more maintainable, scalable code. Study the SOLID principles and learn how to apply them in your code.\n",
      "5. Read books and articles: Stay current with the latest industry trends and best practices by reading books and articles on software engineering. Some recommended books include \"Clean Code\" by Robert C. Martin, \"The Pragmatic Programmer\" by Andrew Hunt and David Thomas, and \"Software Architecture: Patterns, Principles, and Practices\" by Mark Richards.\n",
      "6. Participate in coding challenges: Join online communities like GitHub or CodeWars to participate in coding challenges and practice your skills in a collaborative environment.\n",
      "7. Take online courses or attend workshops: There are many online courses and workshops available that can help you improve your software engineering skills. Look for courses on platforms like Udemy, Coursera, or edX, or attend local workshops and conferences.\n",
      "8. Learn about testing and debugging: Good software engineers know how to test and debug their code effectively. Study testing methodologies and learn how to use debugging tools to identify and fix errors in your code.\n",
      "9. Learn about agile development methodologies: Many software engineering teams use agile methodologies like Scrum or Kanban to manage their projects. Learn about these methodologies and how they can help you work more efficiently.\n",
      "10. Network with other engineers: Connecting with other software engineers can help you learn new techniques, get feedback on your work, and stay motivated. Attend industry events, join online communities, or participate in hackathons to network with other engineers.\n",
      "\n",
      "Remember, improving your software engineering skills takes time and practice. Be patient, persistent, and always keep learning!\n"
     ]
    }
   ],
   "source": [
    "result = chain.invoke({\"input\": \"how can i improve my software engineering skills?\"})\n",
    "print(result)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "844f2d07-b3d2-4676-b1ae-e42ac0fb7786",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "In step 1, we installed the Ollama framework. This framework allows us to host the model out-of-process and connect to the model using a client interface. The Ollama framework manages the connection to the model and communicating with it.\n",
    "\n",
    "In step 2, we run the Ollama runtime with the argument of `llama2`. The Ollama framework loads the `llama2` 7B model which has 7-billion parameters. The Ollama runtime allows loading bigger `llama2` models along with models from other providers. Once the command runs, it will start hosting the `llama2` model locally and is ready to accept commands.\n",
    "\n",
    "In step 3, we do the necessary imports.\n",
    "\n",
    "In step 4, we initialize the model. In our case we use the `llama2` model and wrap in the Ollama wrapper. The Ollama framework allows us to interface with the model using REST (or WebAPI) calls. The model is already running in a separate process as set up in step 2.\n",
    "\n",
    "In step 5, we initialize a chat prompt template instance using the `ChatPromptTemplate` class. The `from_messages` method takes a series of (message type, template) tuples. The second tuple in the messages array has the *{input}* template. This signifies that this value will be passed later.\n",
    "\n",
    "In step 6, we initialize an output parser. The `StrOutputParser` converts a chat message returned by an LLM instance to a string.\n",
    "\n",
    "In step 7, we initialize the chain. The chain pipes the output of one component to the next. In this instance, the *prompt* is sent to the **llm* and the llm operates on the prompt instance. The output of this operation is a chat message. The chat message is then sent to the output_parser, which converts it into a string. In this step, we only setup the various components of the chain. \n",
    "\n",
    "In step 8, we invoke the chain and print the results. We pass the input argument in a dictionary. We set up the prompt template as a message that had the *{input}* placeholder defined there. As part of the chain invocation, the input argument is passed through to the template. The chain invokes the command. The chain is instructing the LLM to generate the answer to the question it asked via the prompt that we setup previously.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ed29fab-29d0-4ebe-a2a1-bf83886ee74d",
   "metadata": {},
   "source": [
    "# <h1><center>Augment the LLM with external content</center></h1>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6552e1c-cf68-4da8-93f0-497922b4cc94",
   "metadata": {},
   "source": [
    "In this recipe, we will extend upon the previous example and build a chain that passes external content to the LLM and helps it answer questions based on that augmented content. We will first execute the chain without passing any external content and contrast the answer with the results when the chain is augmented with the external content. To keep the recipe simple, we will retrieve data from a recent web page that was published on the web after the model was trained. This ensures that the model is not answering from its memory and we will be able to better appreciate the role of content augmentation as part of this recipe. Let's get started.\n",
    "\n",
    "## How to do it\n",
    "\n",
    "1. Do the necessary imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "330b4616-ea19-4f02-8711-19333cf080fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains.combine_documents import create_stuff_documents_chain\n",
    "from langchain.llms import Ollama\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_community.embeddings import OllamaEmbeddings\n",
    "from langchain_community.vectorstores import DocArrayInMemorySearch\n",
    "\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21613e08-f8c2-443c-b8c7-6f600135d78d",
   "metadata": {},
   "source": [
    "2. Initialize the `llama2` model and `Ollama` based embeddings provider."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "96c69179-7c3f-44a4-b94b-f6cd230eb2f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = Ollama(model=\"llama2\")\n",
    "embeddings_provider = OllamaEmbeddings()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a49574e3-4cc9-4c24-8a55-1001d6bec311",
   "metadata": {},
   "source": [
    "3. In this recipe we will load the wikipedia entry on the 2024 summer olympics. We initialize a WebBaseLoader object and pass it the wikipedia URL for the 2024 summer olympics. We call the *load* method for the loader instance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "81577d57-0728-40c2-8668-2d1e7bb397ea",
   "metadata": {},
   "outputs": [],
   "source": [
    "loader = WebBaseLoader(\n",
    "    [\"https://en.wikipedia.org/wiki/2024_Summer_Olympics\"\n",
    "     ]\n",
    ")\n",
    "\n",
    "docs = loader.load()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09ee443a-5c2b-4417-a6fc-7b8d878a983b",
   "metadata": {},
   "source": [
    "4. Initialize the text splitter instance of the type `RecursiveCharacterTextSplitter`. Use the text splitter instance to split the documents into chunks. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7ac89676-9b26-43f0-b0d8-b81d2ef29ae2",
   "metadata": {},
   "outputs": [],
   "source": [
    "text_splitter = RecursiveCharacterTextSplitter()\n",
    "document_chunks = text_splitter.split_documents(docs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "daed8f6c-17de-4b52-9e04-6ba62f5b5fff",
   "metadata": {},
   "source": [
    "5. We initialize a in-memory vector or embeddings store from the document chunks that we created in the previous step. We pass it the document chunks and the embeddings provider."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7df94824-bd38-43c5-b57b-c8e134428a35",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector_store = DocArrayInMemorySearch.from_documents(document_chunks, embeddings_provider)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4ab9508-b47e-45b7-ab47-2f383a6e423b",
   "metadata": {},
   "source": [
    "6. Define a question-answer template and instantiate a `ChatPromptTemplate` instance from the template."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "52a2d630-faee-41bf-b35c-8f91f0af48a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "template = \"\"\"Answer the question based only on the following context:\n",
    "                {context}\n",
    "\n",
    "                Question: {question}\n",
    "                \"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "312cf507-e02c-4c23-90cd-44ff906535ce",
   "metadata": {},
   "source": [
    "7. Initialize the vector store retriever and the output parser. The retriever will provide the augmented content to the chain via the vector store."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "96eca3ed-2290-4762-961b-b8dacba28299",
   "metadata": {},
   "outputs": [],
   "source": [
    "retriever = vector_store.as_retriever(\n",
    "                search_type=\"similarity\"\n",
    "            )\n",
    "output_parser = StrOutputParser()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40f22f47-3a6f-41dc-bf33-e14827d4e771",
   "metadata": {},
   "source": [
    "8. We initialize an instance of `RunnableParallel` so that the arguments can be passed across the various components of the chain in an implicit manner. Then we initialize the chain that chains together the prompt, llm and the output parser along with the `RunnableParallel` instance in the beginning of the chain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "08969c2e-b89e-4c85-842d-3b58376d2a11",
   "metadata": {},
   "outputs": [],
   "source": [
    "setup_and_retrieval = RunnableParallel(\n",
    "    {\"context\": retriever, \"question\": RunnablePassthrough()}\n",
    ")\n",
    "chain = setup_and_retrieval | prompt | llm | output_parser\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1353b461-702e-4097-93fa-3acd8bd2ccc9",
   "metadata": {},
   "source": [
    "9. Invoke the chain and print the results. As we observe in this case, the answers returned by the chain are accurate, though I am skeptical whether BreakDancing is indeed a sport as returned in the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "05a6fedc-f47c-4165-b173-37bc98697ff3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The 2024 Summer Olympics are being held in Paris, France.\n"
     ]
    }
   ],
   "source": [
    "result = chain.invoke(\"Where are the 2024 summer olympics being held?\")\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "364be244-9c25-4805-9eaf-cad1cf164555",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Answer: The 2024 Summer Olympics are expected to feature several new sports and disciplines, including:\n",
      "\n",
      "1. Surfing: After making its debut at the 2016 Rio Olympics, surfing will return as a full medal sport in 2024.\n",
      "2. Skateboarding: Skateboarding was introduced as an Olympic sport for the first time in 2020, and it will continue to be featured at the 2024 Games.\n",
      "3. Rock climbing: Rock climbing made its Olympic debut in 2020, and it is expected to return in 2024.\n",
      "4. Breaking: Also known as breakdancing, breaking will make its Olympic debut in 2024 as a competitive dance sport.\n",
      "5. Karate: Karate was added to the Olympic program in 2018 and will be featured at the 2024 Games.\n",
      "6. Sport climbing: Sport climbing was introduced as an Olympic sport in 2020 and will continue to be featured in 2024.\n",
      "7. Urban mountain biking: This new discipline is a variation of mountain biking that takes place on urban terrain, such as parks and streets.\n",
      "8. 3x3 basketball: This version of basketball features a smaller court and fewer players than traditional basketball.\n",
      "9. Skateboarding park: This event will feature skateboarders competing in a park setting, with a focus on tricks and style.\n",
      "10. BMX freestyle: BMX freestyle will make its Olympic debut in 2024, with riders competing in a variety of events, including street and park competitions.\n",
      "\n",
      "These new sports and disciplines are expected to add excitement and diversity to the 2024 Summer Olympics, while also providing opportunities for athletes from around the world to compete at the highest level.\n"
     ]
    }
   ],
   "source": [
    "result = chain.invoke(\"What are the new sports that are being added for the 2024 summer olympics?\")\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd69a7d5-4d64-4226-b78b-c13499936bf2",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "In step 1, we do the necessary imports. \n",
    "\n",
    "In step 2, we load the `llama2` model via the Ollama wrapper. The model make a call to the Ollama runtime in the later steps. We also load the embedding provider via the `OllamaEmbeddings` instance. The provider connects to the running instance of the Ollama runtime via REST. The default value for the embeddings model is `llama2`. It is a requirement that the Ollama runtime has loaded the `llama2` model and is running in the background when we make call to via the chain execution later. The model (or LLM) and the embeddings provider depend on it.\n",
    "\n",
    "In step 3, we load the URLs via the WebBaseLoader class. It extracts the HTML content and the main content on each HTML page is parsed. The *load* method on the loader instance triggers the extraction of the content from the URLs.\n",
    "\n",
    "In step 4, we initialize the text splitter instance and call the *split_documents* method on it. This splitting of the document is needed step as an LLM can only operate on a context of a limited length. For some large documents, the length of the document exceeds the maximum context length supported by the LLM. Breaking a document into chunks and using that to match the query text allows us to retrive more relevant parts from the document. The `RecursiveCharacterTextSplitter` splits the document based on newline, spaces, and double-newline characters.\n",
    "\n",
    "In step 5, we initialize a vector store. This vector store used in our recipe is an in-memory store. It is recommended that a vector database with persistency support is used in production. To keep this example self-contained and require fewer components, we decided to use an in-memory vector store. We initialize the vector store with the document chunks and the embeddings provider. The vector store creates embeddings of the documents chunks and stores them along with the document metadata. For production grade applications, we recommend the reader to visit the URL: https://python.langchain.com/docs/integrations/vectorstores/ and select a vector store based on their requirements. The Langchain framework is versatile and works with a host of prominent vector stores.\n",
    "\n",
    "In step 6, we define a chat template and create an instance of `ChatPromptTemplate` from it. This prompt template instructs the LLM to answer the question for the given context. This context is provided by the augmentation step.\n",
    "\n",
    "In step 7, we initialize a retriever and an output parser. For the retriever, we call the *as_retriever* method of the vector store instance. The retriever returned by the method is used to retrieve the content from the vector store. The *as_retriever* method is passed an argument `search_type` with the value *similarity*, which is also the default option.. This means that the vector store will be searched against the question text based on similarity. The other options supported are *mmr*, which penalizes search results of the same type and returns diverse results, and *similarity_score_threshold* which operates the same as the *similarity* search type, but can filter out the results based on a threshold. These option also support an accompanying dictionary argument that can be used to tweak the searching parameters. We recommend that the readers refer to the Langchain documentation and tweak the parameters based on their requirements and empirical findings.\n",
    "\n",
    "In step 8, we set up the chain. The chain sequence starts with the `setup_and_retrieval` component that sets up the retriever as context provider. The *question* argument is assumed to be passed later on by the chain. The next component is the `prompt`, which is supplied the context value and the populated prompt is sent to the `llm`. The llm pipes or forwards the results to the string output_parser, which is designed to return the string contained in the output of the llm. There is no execution happening in this step. We are only setting up the chain.\n",
    "\n",
    "In steps 9 and 10, we invoke the chain with two different questions. For each invocation, the question text is matched by similarity against the vector store, the relevant document chunks are returned, followed by the llm using these document chunks as context and using it to answer the respective questions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9ba33b1-189b-472d-a362-f51feb48e062",
   "metadata": {},
   "source": [
    "# <h1><center>Create a Chatbot using an LLM</center></h1>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "480c26f4-9e06-4023-9ecc-9bdec70b059e",
   "metadata": {},
   "source": [
    "In this recipe, we will create a chatbot using the Langchain framework. We learnt in the previous recipe on how to ask questions to an LLM based on a piece of content. Though the LLM was able to answer questions accurately, the interaction with the LLM was completely stateless. The LLM is looking at each question in isolation and ignores any previous interactions or questions that it was asked. In this recipe, we will use an LLM to create a chat interaction, where it will be aware of the previous conversations and use the context from them to answer subsequent questions.\n",
    "\n",
    "## How to do it"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b734ec26-0db5-4dfb-95b4-b6170a509228",
   "metadata": {},
   "source": [
    "1. Do the necessary imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c464f21f-d343-43df-9000-50358bd7e8e1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.llms import Ollama\n",
    "import bs4\n",
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "from langchain_core.messages import AIMessage, HumanMessage, BaseMessage\n",
    "from langchain_community.vectorstores import DocArrayInMemorySearch\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain_community.embeddings import OllamaEmbeddings\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_community.document_loaders import WebBaseLoader"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5928d84b-6528-45d4-8d8a-193e95165257",
   "metadata": {},
   "source": [
    "2. Initialize the `llama2` model and `Ollama` based embeddings provider."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5449cf13-4dcf-493f-b3e1-c45dd4aecf63",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = Ollama(model=\"llama2\")\n",
    "embeddings_provider = OllamaEmbeddings()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "673e987f-05fc-4ad1-a397-c493866a07a3",
   "metadata": {},
   "source": [
    "3. In this recipe we will load a web page that has the content based on which we would want to ask questions. You are open to choose any webpage of your choice. We initialize a WebBaseLoader object and pass it the URL. We call the *load* method for the loader instance. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e7ada4bd-8866-40a8-b06f-0e6a7f9bcc76",
   "metadata": {},
   "outputs": [],
   "source": [
    "loader = WebBaseLoader(\n",
    "    [\"https://lilianweng.github.io/posts/2023-06-23-agent/\"\n",
    "     ]\n",
    ")\n",
    "\n",
    "docs = loader.load()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "967a1653-4ebe-4234-9bfd-fa906f3711e5",
   "metadata": {},
   "source": [
    "4. Initialize the text splitter instance of the type `RecursiveCharacterTextSplitter`. Use the text splitter instance to split the documents into chunks. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "9fe65744-bc63-4894-b90f-119596cf3a41",
   "metadata": {},
   "outputs": [],
   "source": [
    "text_splitter = RecursiveCharacterTextSplitter()\n",
    "document_chunks = text_splitter.split_documents(docs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc9ca970-2d18-478f-8f2e-61af4882f3d7",
   "metadata": {},
   "source": [
    "5. We initialize a in-memory vector or embeddings store from the document chunks that we created in the previous step. We pass it the document chunks and the embeddings provider. We also initialize the vector store retriever and the output parser. The retriever will provide the augmented content to the chain via the vector store."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9046dfb8-0d97-40d7-ae26-bed550a7ae88",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector_store = DocArrayInMemorySearch.from_documents(document_chunks, embeddings_provider)\n",
    "retriever = vector_store.as_retriever()\n",
    "output_parser = StrOutputParser()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad8f2c68-a05b-4665-baf3-1ea772252cc0",
   "metadata": {},
   "source": [
    "6. We define a system prompt that instructs the system to formulate a question based on the previous chat context."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "eab2a9d5-dbfc-4c12-b45a-188124b0a26b",
   "metadata": {},
   "outputs": [],
   "source": [
    "contextualize_q_system_prompt = \"\"\"Given a chat history and the latest user question \\\n",
    "which might reference context in the chat history, formulate a standalone question \\\n",
    "which can be understood without the chat history. Do NOT answer the question, \\\n",
    "just reformulate it if needed and otherwise return it as is.\"\"\"\n",
    "\n",
    "contextualize_q_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", contextualize_q_system_prompt),\n",
    "        MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "        (\"human\", \"{question}\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e775931c-4950-4e64-9a1c-e4c47f3f07cb",
   "metadata": {},
   "source": [
    "7. We initialize a contexualized chain that uses the contextual system prompt created in the previous step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "819b138f-de86-49d2-a11e-f81cac3e56e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "contextualize_q_chain = contextualize_q_prompt | llm | output_parser"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ff90c79-688a-4b11-af65-44dc40dfa46e",
   "metadata": {},
   "source": [
    "8. We initialize another system prompt. This system prompt is setup to use chat history to answer questions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "002f7390-ed80-4204-a983-e662eac7b9b4",
   "metadata": {},
   "outputs": [],
   "source": [
    "qa_system_prompt = \"\"\"You are an assistant for question-answering tasks. \\\n",
    "Use the following pieces of retrieved context to answer the question. \\\n",
    "If you don't know the answer, just say that you don't know. \\\n",
    "Use three sentences maximum and keep the answer concise.\\\n",
    "\n",
    "{context}\"\"\"\n",
    "qa_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", qa_system_prompt),\n",
    "        MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "        (\"human\", \"{question}\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bef05823-5584-485e-a853-806a971baf8e",
   "metadata": {},
   "source": [
    "9. We initialize two helper methods. The `contextualized_question` method returns the contextualized chain if a chat history exists, else it returns the input question. The `format_docs` method concatenates the page content for each document separated by two newline characters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "10bfdd7a-2094-444e-9da0-a2135c53904f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def contextualized_question(input: dict):\n",
    "    if input.get(\"chat_history\"):\n",
    "        return contextualize_q_chain\n",
    "    else:\n",
    "        return input[\"question\"]\n",
    "\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d3c5189-ad5d-4469-9147-a8df158c6404",
   "metadata": {},
   "source": [
    "10. We initialize a chain that uses the contextualized question as the input, pipes it to the retriever  ********"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "2b2ed080-fe18-4920-8c6f-d28b91498deb",
   "metadata": {},
   "outputs": [],
   "source": [
    "rag_chain = (\n",
    "        RunnablePassthrough.assign(\n",
    "            context=contextualized_question | retriever | format_docs\n",
    "        )\n",
    "        | qa_prompt\n",
    "        | llm\n",
    ")\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53207b69-84e1-4a76-94e6-09cf5afa4fd6",
   "metadata": {},
   "source": [
    "11. We initialize a chat_history and invoke the chain to answer a question. We extend the chat history sequence with the returned answer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "c4fae2c6-dca3-4081-bd58-c449ee0d658b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A large language model (LLM) is a type of artificial intelligence (AI) model that is trained on a large corpus of text data to generate language outputs that are coherent and natural-sounding. The model can be used for a variety of applications, such as text generation, language translation, and conversational dialogue.\n",
      "\n",
      "The key characteristics of an LLM include:\n",
      "\n",
      "1. Large scale: LLMs are trained on vast amounts of text data, typically in the range of tens to hundreds of millions of words. This allows the model to learn patterns and relationships in language that would be difficult or impossible to capture with smaller models.\n",
      "2. Deep learning architecture: LLMs typically use deep learning architectures, such as transformer-based models, to process and generate text. These models are trained using backpropagation and other machine learning techniques to optimize the model's performance.\n",
      "3. Multi-task learning: Many LLMs are designed to perform multiple tasks simultaneously, such as language translation, text summarization, and language generation. This allows the model to learn a wide range of linguistic patterns and relationships.\n",
      "4. Flexibility and adaptability: LLMs are often designed to be flexible and adaptable, allowing them to generate text that is context-dependent and tailored to specific tasks or applications.\n",
      "5. Ability to generalize: LLMs are trained on large datasets of text, which allows them to learn patterns and relationships that can be applied to a wide range of linguistic contexts. This enables the model to generate text that is coherent and natural-sounding, even when the input context is unfamiliar or unexpected.\n",
      "6. Improved performance with larger datasets: As the size of the training dataset increases, so does the model's ability to generalize and perform well on unseen data. This makes LLMs increasingly effective as the size of the training dataset grows.\n",
      "7. Potential for creative applications: LLMs have the potential to be used in a wide range of creative applications, such as generating new forms of poetry or fiction, or even creating entirely new languages.\n",
      "8. Ability to learn from human feedback: Many LLMs are designed to incorporate human feedback and corrections, which allows them to learn and improve over time. This can help the model generate more coherent and natural-sounding text.\n",
      "9. Capability to handle complex language tasks: LLMs are capable of handling complex language tasks, such as understanding nuanced meanings and contextual relationships in text, and generating text that is sensitive to these subtleties.\n",
      "10. Continued advancements in technology: As machine learning and natural language processing technologies continue to evolve, so too will the capabilities of LLMs, enabling them to perform an even wider range of tasks and applications.\n"
     ]
    }
   ],
   "source": [
    "chat_history = []\n",
    "\n",
    "question = \"What is a large language model?\"\n",
    "ai_msg = rag_chain.invoke({\"question\": question, \"chat_history\": chat_history})\n",
    "print(ai_msg)\n",
    "chat_history.extend([HumanMessage(content=question), AIMessage(content=ai_msg)])\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94ad6ac1-85db-455f-84a2-89c25bb3251a",
   "metadata": {},
   "source": [
    "12. We invoke the chain again with a second question that needs contextualizing. We provide the chain with the chat history and print the answer to the second question."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "838d1946-95a3-4b71-83e3-18e1f997d934",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sure! The term \"large\" in the context of large language models (LLMs) refers to the scale and scope of the training data used to train these models. Here are some reasons why LLMs are called \"large\":\n",
      "\n",
      "1. Volume of training data: LLMs are trained on vast amounts of text data, typically ranging from tens to hundreds of millions of words. This allows the model to learn patterns and relationships in language that would be difficult or impossible to capture with smaller models.\n",
      "2. Depth of training data: In addition to the sheer volume of data, LLMs are often trained on diverse and representative datasets that cover a wide range of linguistic styles, genres, and contexts. This helps ensure that the model can handle complex language tasks and generate coherent text in a variety of settings.\n",
      "3. Complexity of model architecture: LLMs typically use deep learning architectures with many layers and parameters, which allow them to learn complex patterns and relationships in language. These models are often more complex than smaller models, which makes them more effective at handling complex language tasks.\n",
      "4. Multi-task learning capabilities: Many LLMs are designed to perform multiple tasks simultaneously, such as language translation, text summarization, and language generation. This allows the model to learn a wide range of linguistic patterns and relationships, making it more flexible and adaptable in different contexts.\n",
      "5. Adaptability to new data: As new data becomes available, LLMs can be fine-tuned to adapt to these changes and improve their performance over time. This makes them increasingly effective as the size of the training dataset grows.\n",
      "6. Improved generalization capabilities: With larger datasets, LLMs can learn more abstract and generalizable patterns in language, which allows them to generate text that is more coherent and natural-sounding even when the input context is unfamiliar or unexpected.\n",
      "7. Creative potential: The sheer scale of LLMs gives them the potential to be used in a wide range of creative applications, such as generating new forms of poetry or fiction, or even creating entirely new languages.\n",
      "8. Ability to incorporate human feedback: Many LLMs are designed to incorporate human feedback and corrections, which allows them to learn and improve over time. This can help the model generate more coherent and natural-sounding text.\n",
      "9. Continued advancements in technology: As machine learning and natural language processing technologies continue to evolve, so too will the capabilities of LLMs, enabling them to perform an even wider range of tasks and applications.\n",
      "\n",
      "In summary, the term \"large\" in the context of LLMs refers to the scale and scope of the training data used to train these models, as well as their complexity and multi-task learning capabilities.\n"
     ]
    }
   ],
   "source": [
    "second_question = \"Can you explain the reasoning behind calling it large?\"\n",
    "second_answer = rag_chain.invoke({\"question\": second_question, \"chat_history\": chat_history})\n",
    "print(second_answer)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9acbe1ec-3450-4d92-8e3b-c74372dc2764",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "For steps 1-5, we perform the same steps as we did in the recipe prior to this. To avoid repetition, we ask the reader to refer to the explanations in the *How it works* section of the previous recipe.\n",
    "\n",
    "In step 6, we initialize a contextualized system prompt. A *system* prompt defines the persona and the instruction that is to be followed by the LLM. In this case, we use the system prompt to contain the instruction that the LLM has to use the chat history to formulate a standalone question. We initialize the prompt instance with the system prompt definition and set it up with the expectation that it will have access to the *chat_history* variable that will be passed to it at run-time. We also set it up with the *question* template that will also be passed at run-tume. \n",
    "\n",
    "In step 7, we initilize the contextualized chain. This chain uses the chat history and a given follow-up question from the user and sets up the context for it. As you saw in step 11 and 12 previously, where we asked a simple question followed by another question. The subsequent question did not provide any context and just referred to it as `it`. \n",
    "\n",
    "In step 8, we initialize a system prompt similar to what we did in the previous recipe based on RAG. This prompt just sets up a prompt template. However, we pass this prompt a contextualized question as the chat history grows. This prompt always answers a contextualized question, barring the first one.\n",
    "\n",
    "In step 9, we define two helper functions that are passed to the RAG chain created downstream. The `contextualized_question` method returns the original question as posed by the user if the chat history is not present. This is the typical scenario for the first question. Once the chat_history is present, it returns the contextualized chain. The `format_docs` method just appends the content of all the repository docs separated by newlines.\n",
    "\n",
    "In step 10, we set up a chain. We use the `RunnablePassthrough` class to set up the context. The `RunnablePassthrough` class by definition allows us to pass the input or add additional data to the input via dictionary values. The `assign` method will take a key and will assign the value to this key. In this case, the key is `context` and the assigned value for it is the result of the chained evaluation of the *contextualized question*, the *retriever*, and the *format_docs*. Putting that in the perspective of the whole recipe, for the first question, the context will the set of matched records for the question. For the second question, the context will use the *contextualized question* from the chat history, retrieve a set of matching records and pass that as the context.\n",
    "\n",
    "In step 11, we initialize a chat history array. We ask a simple question to the chain by invoking it. What happens internally is that the question in this case is the contextualized question since there is no chat history present at this point. The rag_chain just answers the question simply and prints the answer. We also extend the chat_history with the returned message. \n",
    "\n",
    "In step 12, we ask the chain a subsequent question without providing much contextual cue. Internally, the `rag_chain` and the `contextualize_q_chain` work in tandem to answer this question. The `contextualize_q_chain` uses the chat history to add more context to the question, retrieves matched records, and sends that as context to the `rag_chain`. The `rag_chain` used the context and the contextualized question to answer the subsequent question.\n",
    "\n",
    "**Note**: We provided a basic workflow on how to execute RAG based flows. We recommend that the reader refer the langchain documentation and use the necessary components to run solutions in production. Some of these would be, using a persistent vector DB store, using concrete types like `BaseChatMessageHistory:` and `RunnableWithMessageHistory` to better manage chat histories. Also, use LangServe to expose endpoints to serve requests."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fddf50b2-18a9-45e0-8eb4-e9d2a285e0a2",
   "metadata": {},
   "source": [
    "# <h1><center>Generate code using an LLM</center></h1>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b218aa8-4482-40dd-a096-bbbe90531de1",
   "metadata": {},
   "source": [
    "In this recipe we will explore how as LLM can be used to generate code, and we will validate it by executing the generated code.\n",
    "\n",
    "\n",
    "\n",
    "## How to do it"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba3362a0-a795-4187-853b-b526e56ca41f",
   "metadata": {},
   "source": [
    "1. Do the necessary imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "22eb79a5-85f2-408d-9d45-5e6aff07671e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import (\n",
    "    ChatPromptTemplate,\n",
    ")\n",
    "from langchain_experimental.utilities import PythonREPL\n",
    "\n",
    "from langchain.llms import Ollama"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce017f14-b14e-4bb8-8400-abcaa3429d45",
   "metadata": {},
   "source": [
    "2. Define a template and initialize a prompt template from it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5cd5b369-82ae-4247-9ddb-7196a0c2c39d",
   "metadata": {},
   "outputs": [],
   "source": [
    "template = \"\"\"Write some python code to solve the user's problem. \n",
    "\n",
    "Return only python code in Markdown format, e.g.:\n",
    "\n",
    "```python\n",
    "....\n",
    "```\"\"\"\n",
    "prompt = ChatPromptTemplate.from_messages([(\"system\", template), (\"human\", \"{input}\")])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed920bd2-56e5-4020-8e61-705407385c42",
   "metadata": {},
   "source": [
    "3. Initilize the model. In this instance, as we are working to generate code, we choose to use the `codellama` model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "aac9ce09-29ba-4368-9d68-b1f81a266e58",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Ollama(model=\"codellama\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd173f74-24a4-4185-8431-d26e22210337",
   "metadata": {},
   "source": [
    "4. We initialize the chain with the prompt and the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8b10b001-971f-4eda-b897-6e69dd9ca3d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = prompt | model | StrOutputParser()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e44a4a10-dd73-42f4-b287-c56f68807f10",
   "metadata": {},
   "source": [
    "5. We invoke the chain and print the result. As we can observe from the output, then code generated by the model looks fairly good."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e202a491-0dec-4156-ac56-4d5cddf1da64",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "```\n",
      "class Node:\n",
      "    def __init__(self, value):\n",
      "        self.value = value\n",
      "        self.left = None\n",
      "        self.right = None\n",
      "\n",
      "def inorder_traversal(root):\n",
      "    if root is None:\n",
      "        return []\n",
      "    return inorder_traversal(root.left) + [root.value] + inorder_traversal(root.right)\n",
      "\n",
      "root = Node(1)\n",
      "root.left = Node(2)\n",
      "root.right = Node(3)\n",
      "print(inorder_traversal(root)) # Output: [2, 1, 3]\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "result = chain.invoke({\"input\": \"write a program to print a binary tree in an inorder traversal\"})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63edb3d1-5d9d-44a6-a201-6c1391fbbf28",
   "metadata": {},
   "source": [
    "6. Let's attempt to do something different."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "1c958e9c-27a0-4253-8ea3-a1a1f0cdc7fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "```python\n",
      "import hashlib\n",
      "\n",
      "def generate_aes_hash(input_str):\n",
      "    \"\"\"Generate a 512-bit AES hash from a given input string\"\"\"\n",
      "    return hashlib.sha512(input_str.encode()).hexdigest()\n",
      "```\n",
      "This function takes a string as an input and returns a hexadecimal representation of the SHA-512 hash of that string using the `hashlib` module in Python. The resulting hash will be 512 bits long, which is the length of a SHA-512 hash.\n"
     ]
    }
   ],
   "source": [
    "result = chain.invoke({\"input\": \"write a program to generate a 512-bit AES hash\"})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b1eb5cd-122d-4e22-b5cf-ab030caeb64e",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "In step 1, we do the necessary imports.\n",
    "\n",
    "In step 2, we define a template. This template defines the instruction or the system prompt that is sent to the model as the task description. In this case, the template defines an instruction to generate python code based on user's requirement. We use this template to initialize a prompt object. The initilized object is of the type `ChatPromptTemplate`. This object lets us send requirements to the model in an interactive way. We can converse with the model based on our instructions to generate several code snippets without having to load the model each time.\n",
    "\n",
    "In step 3, we initialize the `codellama` model. The model loaded via the Ollama framework loads the trained `Llama 2` model. This model trains the `Llama2` model on code specific datasets and supports code generation in Python, C++, Java, C# and more.\n",
    "\n",
    "In step 4, initiliaze the chain with the prompt, model and then output parser.\n",
    "\n",
    "In step 5, we invoke the chain and print the generated code. The generated code is reasonably good, with the Node class having a constructor along the *inorder_traversal* helper method. It also prints out the instruction to use the class.\n",
    "\n",
    "In step 6, we invoke the chain with another requirement and print the generated code. The generation in this case is acceptable too. The model is smart enough to even generate the necessary import statement for the *hashlib* library. \n",
    "\n",
    "So far, we have used the `codellama model` generate the code and the generation was reasonably good. However, we caution the reader to not just trust the generated output as is. Utmost care should be taken to validate the functionality of the generated code via a suite of unit,integration and functional tests for production scenarios. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d446afb6-b84a-4c1c-8f24-2efe7b53ef20",
   "metadata": {},
   "source": [
    "# There's more\n",
    "\n",
    "So far, we have used locally hosted models for generation. Let's see how the `ChatGPT` model from Open AI fairs in this regard. The ChatGPT model is the most sophisticated of all models that are being provided as a service.\n",
    "\n",
    "We only need to change what we do in step 3. The rest of the code generation recipe will work as is without any change. The change for step 3 is a simple 3-step process.\n",
    "\n",
    "a. Add the necessary import statement to your list of imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "32da7ce8-315a-4a29-88b2-513dbeb6e545",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a1a6641-eb8b-48bf-a9d2-53893b6fcac8",
   "metadata": {},
   "source": [
    "b. Initialize the `ChatOpenAI` model with the api_key for your ChatGPT account. Though, ChatGPT is free to use via the browser, tha API usage requires a key and requires account credits to make calls to it. Please refer to the documentation at https://openai.com/blog/openai-api for more information. You can store the api_key in a environment variable and read it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "dfd077dc-5acb-434f-a5aa-3cbe5659bd8e",
   "metadata": {},
   "outputs": [],
   "source": [
    "api_key = os.environ.get('OPENAI_API_KEY')\n",
    "model = ChatOpenAI(openai_api_key=api_key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7874e300-6554-4864-bf51-b683f4a2826a",
   "metadata": {},
   "source": [
    "c. Invoke the chain. As we observe that the code generated by ChatGPT is more reader friendly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "814705cc-3d2f-4c58-8481-a31374de6e3d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sure! Here's an example of how you can generate a 512-bit AES hash using the `hashlib` module in Python:\n",
      "\n",
      "```python\n",
      "import hashlib\n",
      "\n",
      "# Create a new AES hash object\n",
      "aes_hash = hashlib.new('sha512')\n",
      "\n",
      "# Update the hash object with your data\n",
      "data = b'Hello, World!'\n",
      "aes_hash.update(data)\n",
      "\n",
      "# Get the hex representation of the 512-bit hash\n",
      "hash_hex = aes_hash.hexdigest()\n",
      "\n",
      "print(\"512-bit AES hash:\", hash_hex)\n",
      "```\n",
      "\n",
      "This code uses the SHA-512 algorithm, which generates a 512-bit hash. You can update the `data` variable with the input you want to hash.\n"
     ]
    }
   ],
   "source": [
    "result = chain.invoke({\"input\": \"write a program to generate a 512-bit AES hash\"})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "564e5cde-224f-44c5-af92-5b055a8b6434",
   "metadata": {},
   "source": [
    "# <h1><center>Generate a SQL query using human defined requirements</center></h1>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f124657c-3706-4eaa-b950-f9f93ee196d2",
   "metadata": {},
   "source": [
    "In this recipe, we will learn how to use an LLM to infer the schema of a database and generate SQL queries based on human input. The human input would be a simple question. The LLM will use the schema information along with the human question to generate the correct SQL query. Also, we will connect to a database that has populated data, execute the generated SQL query and present the answer in a human readable format. Let's get started.\n",
    "\n",
    "For this recipe we will use a SQLite3 DB. Please follow the instructions on https://github.com/jpwhite3/northwind-SQLite3 to setup the DB locally.\n",
    "\n",
    "1. Do the necessary imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "1cab5cdf-b2de-427c-9b42-10140c28cb24",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "\n",
    "from langchain_community.utilities import SQLDatabase\n",
    "\n",
    "from langchain.llms import Ollama\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "import os"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "379b04b3-47b3-4b73-a780-c3bffcb98cda",
   "metadata": {},
   "source": [
    "2. We define the prompt template and create a `ChatPromptTemplate` instance using it. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "b9901a61-5692-4160-82ec-500572a986c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "template = \"\"\"You are a SQL expert. Based on the table schema below, write just the SQL query without the results that would answer the user's question.:\n",
    "{schema}\n",
    "\n",
    "Question: {question}\n",
    "SQL Query:\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cec1915d-7f91-4846-843f-9ae0322a484e",
   "metadata": {},
   "source": [
    "3. We connect to the local DB running on your machine and get the database handle. In this recipe, we are using a file based DB that resides locally on the filesystem. Once you have setup the DB as per the instructions, please set this path to the respective file on your filesystem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "be4ec23c-1d0e-475a-a58a-29a35cc93c48",
   "metadata": {},
   "outputs": [],
   "source": [
    "db = SQLDatabase.from_uri(\"sqlite:///db/northwind-SQLite3/dist/northwind.db\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2e5c070-5fcf-4af4-9e90-65689290de1a",
   "metadata": {},
   "source": [
    "4. We define a method that gets will get the schema information using the database handle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "48049322-fdad-4ea9-a15f-0b88c2a6497b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_schema(_):\n",
    "    return db.get_table_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02c0da4e-1b44-43a3-899f-a859ff6148d9",
   "metadata": {},
   "source": [
    "5. We define a method that will run a query using the database handle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "985e1cca-0b6a-418d-9f3d-e998cd7abce0",
   "metadata": {},
   "outputs": [],
   "source": [
    "def run_query(query):\n",
    "    return db.run(query)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "882ceb82-0e6e-4f4f-aedf-4744fa9d501a",
   "metadata": {},
   "source": [
    "6. We read the OpenAI API key from the environment variable and initialize the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d08ce006-91e4-4663-a340-fda12c3175f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "api_key = os.environ.get('OPENAI_API_KEY')\n",
    "model = ChatOpenAI(openai_api_key=api_key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a736f1de-7e43-44a1-aa5d-2cf548f4b22b",
   "metadata": {},
   "source": [
    "7. We create a chain that uses the database schema, the prompt, the model and an output parser."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "a0c637bb-5401-4cab-8afd-6f3395bfecae",
   "metadata": {},
   "outputs": [],
   "source": [
    "sql_response = (\n",
    "    RunnablePassthrough.assign(schema=get_schema)\n",
    "    | prompt\n",
    "    | model.bind(stop=[\"\\nSQLResult:\"])\n",
    "    | StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c99c53f5-e915-4b6a-83b6-21f95073c080",
   "metadata": {},
   "source": [
    "8. We invoke the chain by passing it a simple question. We expect the chain to return a SQL query as part of the response."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "ab0859e7-9246-4ffa-89f4-50f5d241fe6f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'SELECT COUNT(*) FROM Employees'"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sql_response.invoke({\"question\": \"How many employees are there?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "112bbadb-cfd7-4e81-943f-4931faea7beb",
   "metadata": {},
   "source": [
    "9. We invoke another query for further testing the LLM whether it can infer the whole schema."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "023444f0-7586-44da-9486-1af972ab8da0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"SELECT COUNT(*) \\nFROM Employees \\nWHERE HireDate <= DATE('now', '-5 years')\""
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sql_response.invoke({\"question\": \"How many employees have been tenured for more than 11 years?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4a9268b-60d6-423d-a44f-3506003e9ed8",
   "metadata": {},
   "source": [
    "10. We now initialize another template that will instruct the model to use the SQL query and execute it on the database."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "def7e4d1-a584-4618-b71a-58ce84350c8d",
   "metadata": {},
   "outputs": [],
   "source": [
    "template = \"\"\"Based on the table schema below, question, sql query, and sql response, write a natural language response:\n",
    "{schema}\n",
    "\n",
    "Question: {question}\n",
    "SQL Query: {query}\n",
    "SQL Response: {response}\"\"\"\n",
    "prompt_response = ChatPromptTemplate.from_template(template)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8b5a827-8481-4b08-b9e3-33ad3d991229",
   "metadata": {},
   "source": [
    "11. We create a full chain that uses the previous chain to generate the SQL query, and executes that on the database."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "3ebd944b-58e7-4d48-b4b8-9d0f5cfd3bbf",
   "metadata": {},
   "outputs": [],
   "source": [
    "full_chain = (\n",
    "    RunnablePassthrough.assign(query=sql_response).assign(\n",
    "        schema=get_schema,\n",
    "        response=lambda x: run_query(x[\"query\"]),\n",
    "    )\n",
    "    | prompt_response\n",
    "    | model\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d4b6441-391a-42c1-8ea7-50801aab0b96",
   "metadata": {},
   "source": [
    "12. We invoke the full chain with the same human question that we asked it earlier. The chain produces a simple human readable answer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "1a0c6807-de45-452b-a2b5-21211da630d2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='There are 9 employees in the database.'\n"
     ]
    }
   ],
   "source": [
    "result = full_chain.invoke({\"question\": \"How many employees are there?\"})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf2875aa-f328-4647-94da-1c961224feec",
   "metadata": {},
   "source": [
    "13. We invoke the chain with a slighly more complex query. We observe that the simple response from the invocation is indeed accurate. The accompanying screenshot does confirm the accuracy of the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "81d843a8-4e1b-4320-a6f5-98620f64c6aa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='The employees who have been tenured for more than 11 years are Nancy Davolio, Andrew Fuller, and Janet Leverling.'\n"
     ]
    }
   ],
   "source": [
    "result = full_chain.invoke({\"question\": \"Give me the name of employees who have been tenured for more than 11 years?\"})\n",
    "print(result)"
   ]
  },
  {
   "attachments": {
    "f8b07299-f2e0-43ed-bf92-a1fdfca0c688.png": {
     "image/png": "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"
    }
   },
   "cell_type": "markdown",
   "id": "244ad457-aab1-45a5-9912-71f02b03307b",
   "metadata": {},
   "source": [
    "Query results that were returned while querying the database manually using the SQLite command line interface.\n",
    "\n",
    "![image.png](attachment:f8b07299-f2e0-43ed-bf92-a1fdfca0c688.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e0c2195-82c6-45cc-ab8e-45b3a269bc2a",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "In step 1, we do the necessary imports.\n",
    "\n",
    "In step 2, we define a template. This template defines the instruction or the system prompt that is sent to the model as the task description. In this case, the template defines an instruction to generate a SQL statement based on user's requirement. We use this template to initialize a prompt object. The initialized object is of the type `ChatPromptTemplate`. This object lets us send requirements to the model in an interactive way. We can converse with the model based on our instructions to generate several SQL statements without having to load the model each time.\n",
    "\n",
    "In step 3, we get a handle to the database. We will use this handle in the subsequent calls to make connection with the DB and perform operations on it.\n",
    "\n",
    "In step 4, we define a method named *get_schema* that returns the schema information for all the DB objects like tables, indexes. This schema information is used by the LLM in the following calls to infer the table structure and generate queries from it.\n",
    "\n",
    "In step 5, we define a method named *run_query* that runs the query on the DB and returns the results. The results are used by the LLM in the following calls to infer the result from and generate a human readable, friendly answer.\n",
    "\n",
    "In step 6, we read the *api_key* from an environment variable and initialize the *ChatGPT* model. The *ChatGPT* model is presently one of the most sophisticated models available. Our experiments with using *CodeLlama* for this recipe was returning queries with a lot of noisy characters, as opposed to *ChatGPT*, which was precise and devoid of any noise.\n",
    "\n",
    "In step 7, we create a chain that wires the schema, prompt, model and an output parser. The schema is sourced from the *get_schema* method and passed as a dictionary value to the downstream chain components. The prompt uses the schema to fill in the place holder *schema* element in its template. The model receives the prompt and the schema information and generates the query. The *bind* method of the model is *Runnable* sequence. This method cuts off the output from the model at the first instance of the strings that are passed as the parameter to it. Once the model generates the output, it is parsed by the string parser.\n",
    "\n",
    "In step 8, we invoke the chain and print the results. The generated query by the LLM is accurate and it was able to infer the schema and generate the correct query for our requirements.\n",
    "\n",
    "In step 9, we test the chain further by passing it a slightly more complex sure. On observing the results, we can see it can infer our question based on *tenure* and map it to *HireDate* column as part of the schema. This is a very smart inference that was done automatically by `ChatGPT`.\n",
    "\n",
    "In step 10, we define another prompt template that will let us do generate the query and execute it. It will use the chain that we have created so far, and add the execution components in another chain and invoke that chain. Howeve, at this step, we just generate the template and the prompt instance out of it. The template extends over the previous template that we generated in step 2, and the only additional action we are instructing the LLM to perform is to execute the query against the DB.\n",
    "\n",
    "In step 11, we define another chain. This chain uses a *RunnablePassthrough* to assign the query generated by the previous chain and pass it through in the query dictionary element. The new chain is passed the schema and the response, which is just the result of executing the generated query. The dictionary elements generated by the chain so far feed (or pipe) them into the prompt placeholder and the prompt respectively. The model uses the prompt to emit out a results that is simple and human readable.\n",
    "\n",
    "In step 12, we invoke the chain and print the results. We observe that the results are accurate as per the information verified in the DB. The answer generated is simple and human readable.\n",
    "\n",
    "In Step 13, we invoke the chain again and print the results. The LLM is smart enough to generate and execute the query, infer our answer requirements and map their appropriately with the DB schema and return the results. This is indeed quite impressive. We added a reference screenshot of the data in the DB for our readers to show the accuracy of the results.\n",
    "\n",
    "Though the results generated by the LLMs in this recipe are fairly impressive and accurate, we advise the reader to thoroughly verify their queries and results before taking a system to production. Also important is to ensure that no arbitrary SQL queries can be injected via the users by validating the input. It's best to keep a system answering questions to operate in the context of an account with Readonly permissions. \n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b75bb2f1-c1c4-4afe-955f-d8895228b3a1",
   "metadata": {},
   "source": [
    "# <h1><center>Agents - Make an LLM to reason and act</center></h1>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3f902c6-f6c4-4a18-afbd-92e601009348",
   "metadata": {},
   "source": [
    "In this recipe, we will learn how to make an LLM reason and act. The agentic pattern uses the ReAct (Reason and Act) pattern as described in the paper (). We start with creating a few tools with an LLM. These tools internally describe the action they can help with. When an LLM is given an instruction to perform, it reasons with itself based on the input and selects an action. This action maps with a tool that is part of the agent execution chain. The step of reasoning, acting and observing is performed iteratively until the LLM arrives at the correct answer.  In this recipe we will ask a question to the LLM that will make it search the internet for some information and then use that information to perform a mathematical information and return us the final answer. This recipe requires the users to create a free API key at `https://serpapi.com/users/welcome`, so we recommend the reader to create one. Also, we will use ChatGPT and will need the OpenAPI api_key too. Let's gets started.\n",
    "\n",
    "We need to install a couple of libraries before we start with the recipe execution. Execute the following command on the command line interface.\n",
    "\n",
    "`!pip3 install google-search-results numexpr`\n",
    "\n",
    "\n",
    "1. Do the necessary imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cf8a4039-2c24-4288-82a4-71dcf73bc5ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents import AgentType, initialize_agent, load_tools\n",
    "from langchain.agents.tools import Tool\n",
    "from langchain.chains import LLMMathChain\n",
    "from langchain_experimental.plan_and_execute import PlanAndExecute, load_agent_executor, load_chat_planner\n",
    "from langchain.utilities import SerpAPIWrapper\n",
    "from langchain_openai import OpenAI"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d684b70-1b36-43ae-9fdb-2a5dbe01ec67",
   "metadata": {},
   "source": [
    "2. We will load the API keys for `OpenAPI` and `SerpAPI` from the environment variables and use them in the subsequent calls. We also initialize the OpenAPI LLM."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f1ff63a4-8ca2-40bd-b86f-c1ea3780450f",
   "metadata": {},
   "outputs": [],
   "source": [
    "api_key = 'OPEN_API_KEY' # set your OPENAI API key\n",
    "serp_api_key='SERP API KEY' # set your SERPAPI key\n",
    "llm = OpenAI(api_key=api_key, temperature=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49769cff-72d1-4003-9e7b-ef46274b9242",
   "metadata": {},
   "source": [
    "3. We initilialize the search and the math helpers. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "096c181e-1176-42b5-93f6-ee2fdfed0e4d",
   "metadata": {},
   "outputs": [],
   "source": [
    "search_helper = SerpAPIWrapper(serpapi_api_key=serp_api_key)\n",
    "math_helper = LLMMathChain.from_llm(llm=llm, verbose=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a299824-4c40-4798-aa71-ae58103baf49",
   "metadata": {},
   "source": [
    "4. We wrap the search and the math helper using the tool class and add the descriptions for them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "579aea52-d9ef-49aa-a2fc-33b840f22385",
   "metadata": {},
   "outputs": [],
   "source": [
    "search_tool = Tool(name='Search', func=search_helper.run, description=\"use this tool to search for information\")\n",
    "math_tool = Tool(name='Calculator', func=math_helper.run, description=\"use this tool for mathematical calculations\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "142d0024-5524-4ea6-9429-8d603cace1c6",
   "metadata": {},
   "source": [
    "5. We create a tools array using the search and the math tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a33d500e-6a7b-4e3d-814b-c6db1e8818b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [search_tool, math_tool]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1a4704d-8560-41f2-afa5-08f2fe8f28ea",
   "metadata": {},
   "source": [
    "6. We initialize an action planner by calling the `load_chat_planner` function. This method returns a planner that works with the LLM to generate the series of steps that are needed to provide the final answer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "410a6992-a67e-4441-95c4-4fdcacdaa008",
   "metadata": {},
   "outputs": [],
   "source": [
    "action_planner = load_chat_planner(llm)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "954e2485-4b47-423e-83db-9be6221518c3",
   "metadata": {},
   "source": [
    "7. We initialize and action executor by calling the `load_agent_executor` function. This method uses the `LLM` and the tools and weave them together to generate the final result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9e28612c-5e37-4617-afdf-b6be7baa4e33",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_executor = load_agent_executor(llm, tools, verbose=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed460d8b-c460-458e-897f-745e602e4e4e",
   "metadata": {},
   "source": [
    "8. We initiliaze an instance of the `PlanAndExecute` class. This class uses a `planner` and an `executor` to plan and execute a series of steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "1eca1bff-0956-4848-acde-0485f1ac1102",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = PlanAndExecute(planner=action_planner, executor=agent_executor, verbose=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bea6c7c9-df96-47ae-a8ed-6b26ed0a6ec3",
   "metadata": {},
   "source": [
    "9. We invoke the agent and print its results. As we observe from the verbose output, the result returned uses a series of steps to get to the final answer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "54e88853-6eb2-4927-842c-334780f502cc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new PlanAndExecute chain...\u001b[0m\n",
      "steps=[Step(value='Gather data on the number of FIFA world cup wins for Brazil and France.'), Step(value='Calculate the difference between the two numbers.'), Step(value='Output the difference as the answer.\\n')]\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mAction:\n",
      "{\n",
      "  \"action\": \"Search\",\n",
      "  \"action_input\": \"Number of FIFA world cup wins for Brazil and France\"\n",
      "}\n",
      "\n",
      "\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "*****\n",
      "\n",
      "Step: Gather data on the number of FIFA world cup wins for Brazil and France.\n",
      "\n",
      "Response: Action:\n",
      "{\n",
      "  \"action\": \"Search\",\n",
      "  \"action_input\": \"Number of FIFA world cup wins for Brazil and France\"\n",
      "}\n",
      "\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThought: I can use the Calculator tool to subtract the number of wins for France from the number of wins for Brazil.\n",
      "Action:\n",
      "```\n",
      "{\n",
      "  \"action\": \"Calculator\",\n",
      "  \"action_input\": \"5 - 2\"\n",
      "}\n",
      "```\n",
      "\u001b[0m\n",
      "\n",
      "\u001b[1m> Entering new LLMMathChain chain...\u001b[0m\n",
      "5 - 2\u001b[32;1m\u001b[1;3m```text\n",
      "5 - 2\n",
      "```\n",
      "...numexpr.evaluate(\"5 - 2\")...\n",
      "\u001b[0m\n",
      "Answer: \u001b[33;1m\u001b[1;3m3\u001b[0m\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "Observation: \u001b[33;1m\u001b[1;3mAnswer: 3\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m I have the final answer now.\n",
      "Action:\n",
      "```\n",
      "{\n",
      "  \"action\": \"Final Answer\",\n",
      "  \"action_input\": \"The difference between the number of FIFA world cup wins for Brazil and France is 3.\"\n",
      "}\n",
      "```\n",
      "\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "*****\n",
      "\n",
      "Step: Calculate the difference between the two numbers.\n",
      "\n",
      "Response: The difference between the number of FIFA world cup wins for Brazil and France is 3.\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mAction:\n",
      "{\n",
      "  \"action\": \"Final Answer\",\n",
      "  \"action_input\": \"The difference between the number of FIFA world cup wins for Brazil and France is 3.\"\n",
      "}\n",
      "\n",
      "\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "*****\n",
      "\n",
      "Step: Output the difference as the answer.\n",
      "\n",
      "\n",
      "Response: Action:\n",
      "{\n",
      "  \"action\": \"Final Answer\",\n",
      "  \"action_input\": \"The difference between the number of FIFA world cup wins for Brazil and France is 3.\"\n",
      "}\n",
      "\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'How many more FIFA world cup wins does Brazil have compared to France?',\n",
       " 'output': 'Action:\\n{\\n  \"action\": \"Final Answer\",\\n  \"action_input\": \"The difference between the number of FIFA world cup wins for Brazil and France is 3.\"\\n}\\n\\n'}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent.invoke(\"How many more FIFA world cup wins does Brazil have compared to France?\")"
   ]
  },
  {
   "attachments": {
    "169b90e5-e48f-4089-ac12-548dcf8e929e.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAABBcAAADJCAYAAAB4x9p9AAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AAAAtdEVYdENyZWF0aW9uIFRpbWUAVHVlIDA2IEZlYiAyMDI0IDA5OjM4OjU4IFBNIEVTVDxmZnQAACAASURBVHic7N1rlFzVnd/977nXpa9q0UIXq40E3RYDrUAkbkEazFjAeJBxxsLM2Nh+kiGTZ8LYmQTbSR6GNXEwyTyMneSB8cpamGQWRnaGyzi25fEYhG0QHiyQgowgSGouooVES22pr1VddercnhdVrb7o0lXqaknIv89aWtDVp8/edc7e5+z9P3vvY7S2tiaIiIiIiIiIiJwi80xnQERERERERETe3xRcEBEREREREZFZUXBBRERERERERGZFwQURERERERERmRUFF0RERERERERkVhRcEBEREREREZFZUXBBRERERERERGZFwQURERERERERmRUFF0RERERERERkVhRcEBEREREREZFZUXBBRERERERERGZFwQURERERERERmRUFF0RERERERERkVhRcEBEREREREZFZUXBBRERERERERGZFwQURERERERERmRUFF0RERERERERkVhRcEBEREREREZFZUXBBRERERERERGZFwQURERERERERmRUFF0RERERERERkVhRcEBEREREREZFZUXBBRERERERERGZFwQURERERERERmRUFF0RERERERERkVhRcEBEREREREZFZsc90Bs4dHt233MGapbDvmYfZtMs/0xkSOUeobomIiIiInO00cqFumrnin97LvV+5l9tXp850ZkTOKO+2R+gbGGDgR3fSMeu9qW6JiIiIiJztzsrggvfpx8odk2P+7eD+q7ya9rX6K1sZGBhg94PrqO0vZXY81n9z78R5u+7MHX3vpgfYPTDAwN/fS/e0sTr1LGunRwd3/miAgYE+Hvv02Zg/ERERERH5dVTnaREezc0wPFynYct+P737hji6t7CXQ/mzdUh0Pw/fMo+Hz3Q2zhbelaxb3Vz5oYO1H+6GZ7ed0Syd1PuqrP26Ud0SERERETnb1XfkgreWe597ha3fuZ87b+6mebahix0P8vErr+Kq8X//6Da+/iqAx7r/upuBgQF2/NX9PPTcbvr6+9j99P2s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"
    },
    "1cc1c438-d387-4cc4-8b58-42df30e3379f.png": {
     "image/png": "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YM/nDL8zR7ngiX59QOu96Z55a67u0f7lgKhncGsJvqpI392jPdERimOKI6rnMsoLCS5wRNd7tN/sj3fvx+mf3rfXhVvPKb49d/dfNcoyGLvPVxTM/apZPuM3Dr8+5uKGMfvSRzFX92ltloRBRPn1mOBaf/qDEeOU478PaF0pCeOk2fX9gif+7Bjt91F1eBrvi3HqX78ivlXjn80YfqfFcMNgtgbMTdAOsD0k+dRhAoNfjvCmJrrZb47601FGDwiWl5e/bHlXcpTDc3z3esyfcN2XiIiITNu9c3yTN//30efOi4jIVJ3uVOdZcnft2hOU9anZoEtERERERGRWKPB9Wtba7L/2pEPpa+J/uUN7nKMhREREREREZGxKdX5akhB7JnzCquqaYKcinOJ6YREREREREdGM79NTOqKtU7ibsYiIiIiIyIybze15RUREREREREYU+IqIiIiIiMhMO36q8/o8e6/l1K7GbBdkvzgg6UyxZCIiIiIiIiJTcPwZ3+0Bcz/fJ7vh8WdzBt8e8xR0ERERERERkafo+IFvYYmvF2SbQ4yHumXQ9tAiIiIiIiJy2px8ja87DHeDE19KREREREREZNpOHvj6UcgbnvhKIiIiIiIiIlN38sC35zAO6tWU4XpInWjmV0RERERERE6Pkwe+ZUH+q4LQJAxeO0Pnr1pYHZIkIiIiIiIip8TxjzM6FCcUL2W4wJG82yXdsoR+CiUTERERERERmYKT2dRugAAAAiNJREFUB77tGJdCcKdP/v5wClPIIiIiIiIiItNz8jjV0BxjVGpfZxERERERETl9Th743o12dYqviIiIiIiInD7HT3U2AXUU4NdjvAGGXjO+IiIiIiIicuocP/Bda7P/Wo4HcJbs19XUCiUiIiIiIiIyLcHy8vLxcpSTEHvGgKsxuxajuFdEREREREROoePP+JaOaMtNsSgiIiIiIiIi06fTh0RERERERGSmKfAVERERERGRmabAV0RERERERGbaCQJfg/1mi+LFDBc/+jvqMxnFSy2GG4/7mKOvISIiIiIiInISJwh8PeEtj/3mAt0/SptjjR4SbJeEWcrg1UWK1Ued8nv0NUREREREREROIszz/G+P+8PBwBLf9NjvzlNRktx+OHStCW8URHM5/d+LCD8qCe2k1xARERERERE5vuOf4ysiIiIiIiLyJaDNrURERERERGSmKfAVERERERGRmabAV0RERERERGaaAl8RERERERGZaQp8RUREREREZKYp8BUREREREZGZpsBXREREREREZpoCXxEREREREZlpCnxFRERERERkpinwFRERERERkZmmwFdERERERERmmgJfERERERERmWkKfEVERERERGSmKfAVERERERGRmabAV0RERERERGaaAl8RERERERGZaQp8RUREREREZKYp8BUREREREZGZpsBXREREREREZpoCXxEREREREZlpCnxFRERERERkpv0fBNu9/WC0OpkAAAAASUVORK5CYII="
    },
    "dfa011ae-d553-4ae4-b004-2396f13a76b1.png": {
     "image/png": "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"
    }
   },
   "cell_type": "markdown",
   "id": "5d90240e-aec8-4452-812f-ee385ba536f3",
   "metadata": {},
   "source": [
    "# How it works\n",
    "\n",
    "In step 1, we do the necessary imports.\n",
    "\n",
    "In step 2, we read the API keys for OpenAI and Serp API. We initialize the LLM using the *OpenAI* constructor call. We pass in the API key along with the temperature value of `0`. Setting the temperature value to zero ensure a more deterministic output. The LLM chooses a greedy approach where it always chooses the token that has the highest probability of being the next one. We did not specify a model explicitly as part of this call. We recommend the user to refer to the models listed at *https://platform.openai.com/docs/api-reference/models* and choose one of their choice. The default model is set to `gpt-3.5-turbo-instruct` if a model is not specified explicitly.\n",
    "\n",
    "In step 3, we initialize the *search* and *math* helpers. The *search* helper encapsulates or wraps the `SerpAPI` which allows us to perform a web search using *Google*. The *math* helper uses the `LLMMathChain` class. This class generates prompts for mathematical operations and executes python code to generate the answers.\n",
    "\n",
    "In step 4, we use the *search* and *math* helpers initialized in the previous step and wrap them in the `Tool` class. The tool class is initialized with a *name*, *func* and a *description*. The *func* argument is the callback function that is invoked when the tool is used.\n",
    "\n",
    "In step 5, we create a tools array and add the *search* and *math* tools to it. This tools array will be used downstream.\n",
    "\n",
    "In step 6, we initialize an action planner. The planner in this instance has a prompt defined within it. This prompt is of type `system` and as part of the instructions in the prompt, the LLM is supposed to come up with a series of steps or a plan to solve that problem.\n",
    "\n",
    "In step 7, we initialize an agent executor. The agent executor calls the agent, invokes its chosen actions. Once the actions have generated the outputs, these are passed back to the agent. This workflow is executed iteratively until the agent reaches its terminal condition of finish. \n",
    "\n",
    "In step 8, we initialize a `PlanAndExecute` chain and pass it the planner and the executor. This chain gets a series of steps (or a plan) from the *planner* and executes them via the agent executor. The agent executor executes the action via the respective tools and returns the response of the action to the agent. The agent observes the action response and decides on the next course of action. \n",
    "\n",
    "In step 9, we invoke the agent chain. Let's analyze the ouput of the invocation to understand this better.\n",
    "\n",
    "The first step of the plan is to search for the world cup wins for both Brazil and France. The agent decides to execute the *Search* action for this step.\n",
    "\n",
    "![image.png](attachment:dfa011ae-d553-4ae4-b004-2396f13a76b1.png)\n",
    "\n",
    "Once the responses from those queries are available, the agent identifies the next action as the *Calculator* and executes it. \n",
    "\n",
    "![image.png](attachment:169b90e5-e48f-4089-ac12-548dcf8e929e.png)\n",
    "\n",
    "Once the agent identifies it has the final answer, it forms a well generated answer.\n",
    "\n",
    "![image.png](attachment:1cc1c438-d387-4cc4-8b58-42df30e3379f.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ccd0a9e-1f51-450d-b94a-af4c7ffd6347",
   "metadata": {},
   "source": [
    "The completes our chapter for Generative AI and LLMs. Though we have just scratched the surface of whats possible via Generative AI, we hope that the examples presented in this chapter help you to understand the capabilities of LLMs and their relation to Generative AI. We recommend that the reader explore the Langchain site for updates and new tools and agents for their use cases and apply them in production scenarios following the established best practices."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "torchgpu",
   "language": "python",
   "name": "torchgpu"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
